/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./PointsSet.h"

/**************************************************************
* Constructors                                                *
***************************************************************/
PointsSet::PointsSet(): Object3D() {
	mPoints = new PointPair[POINT_BUFFER];
	mReallocCnt = 1;
	mNrOfPoints = 0;
	mDisplayPointsCords = true;
	mColor = Color::ToWinColor(WHITE);
	mLastRenderedColor = mColor;
}

PointsSet::PointsSet(const PointsSet& pointSet) {
	delete [] mPoints;
	mReallocCnt = pointSet.mReallocCnt;
	mNrOfPoints = pointSet.mNrOfPoints;
	mDisplayPointsCords = pointSet.mDisplayPointsCords;
	mColor = pointSet.mColor;
	mLastRenderedColor = pointSet.mLastRenderedColor;
	mPoints = new PointPair[pointSet.mReallocCnt * POINT_BUFFER];
	for (cInt i = 0; i < pointSet.mNrOfPoints; i++) {
		mPoints[i] = pointSet.mPoints[i];
	}
}

PointsSet::~PointsSet() {
	if (mPoints) {
		delete [] mPoints;
	}
}

/**************************************************************
* Operators                                                   *
***************************************************************/
PointsSet& PointsSet::operator=(const PointsSet& pointSet) {
	if (&pointSet != this) {
		if (mPoints) {
			delete [] mPoints;
		}
		mReallocCnt = pointSet.mReallocCnt;
		mNrOfPoints = pointSet.mNrOfPoints;
		mDisplayPointsCords = pointSet.mDisplayPointsCords;
		mColor = pointSet.mColor;
		mLastRenderedColor = pointSet.mLastRenderedColor;
		mPoints = new PointPair[pointSet.mReallocCnt * POINT_BUFFER];
		for (cInt i = 0; i < pointSet.mNrOfPoints; i++) {
			mPoints[i] = pointSet.mPoints[i];
		}
	}

	return *this;
}

/**************************************************************
* Other helpful functions                                     *
***************************************************************/
cVoid PointsSet::Realloc() {
	PointPair* mTempPoints = new PointPair[++mReallocCnt * POINT_BUFFER];
	
	for (cInt i = 0; i < mNrOfPoints; i++) {
		mTempPoints[i].mPoint = mPoints[i].mPoint;
		mTempPoints[i].mSelected = mPoints[i].mSelected;
	}
	delete [] mPoints;

	mPoints = mTempPoints;
}

cVoid PointsSet::AddPoint(Point4D point) {
	if ((mNrOfPoints + 1) > (mReallocCnt * POINT_BUFFER)) {
		Realloc();
	}
	mPoints[mNrOfPoints].mPoint = point;
	mPoints[mNrOfPoints].mSelected = TEMP_SELECTION;
	mNrOfPoints++;
}

cVoid PointsSet::AddPoint(Point4D point, cUShort selected) {
	if ((mNrOfPoints + 1) > (mReallocCnt * POINT_BUFFER)) {
		Realloc();
	}
	mPoints[mNrOfPoints].mPoint = point;
	switch(selected) {
		default:
		case 0:
			mPoints[mNrOfPoints].mSelected = NO_SELECTION;
			break;
		case 1:
			mPoints[mNrOfPoints].mSelected = TEMP_SELECTION;
			break;
		case 2:
			mPoints[mNrOfPoints].mSelected = PERS_SELECTION;
			break;
	}
	mNrOfPoints++;
}

cVoid PointsSet::ReplacePoint(const Point4D& point, cInt iIndex) {
	if (iIndex >= 0 && iIndex < mNrOfPoints) {
		mPoints[iIndex].mPoint = point;
	}
}

cVoid PointsSet::RemovePoint(cInt iIndex) {
	if (iIndex >= 0 && iIndex < mNrOfPoints) {
		for (cInt i = iIndex; i < (mNrOfPoints - 1); i++) {
			mPoints[i] = mPoints[i + 1];
		}
		mNrOfPoints--;
	}
}

Point4D PointsSet::GetPoint(cInt i) {
	return mPoints[i].mPoint;
}

cInt PointsSet::GetNrOfPoints() {
	return mNrOfPoints;
}

cVoid PointsSet::SwitchDisplayPoints() {
	mDisplayPointsCords = !mDisplayPointsCords;
}

cVoid PointsSet::SelectPoint(cInt iIndex, SELECTION_TYPE bSelected) {
	if (iIndex >= 0 && iIndex < mNrOfPoints) {
		if (mPoints[iIndex].mSelected != PERS_SELECTION) {
			mPoints[iIndex].mSelected = bSelected;
		}
	}
}

PointsSet::SELECTION_TYPE PointsSet::SelectPoint(cInt iIndex, cBool bSelect) {
	switch(mPoints[iIndex].mSelected) {
		case NO_SELECTION:
		case TEMP_SELECTION:
			mPoints[iIndex].mSelected = PERS_SELECTION;
			break;
		default:
			mPoints[iIndex].mSelected = TEMP_SELECTION;
			break;
	}

	return mPoints[iIndex].mSelected;
}

cVoid PointsSet::DeselectPoints() {
	for (cInt i = 0; i < mNrOfPoints; i++) {
		mPoints[i].mSelected = NO_SELECTION;
	}
}

cBool PointsSet::ArePointsCordsVisible() {
	return mDisplayPointsCords;
}

/*virtual*/ cVoid PointsSet::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane) {
	Point4D a;
	
	if (mVisible) {
		for (cInt i = 0; i < mNrOfPoints; i++) {
			a = mPoints[i].mPoint;
			a = matrix * mMatrix * mObjectMatrix * a;
			if (a.mW < plane.GetNearClippingPlane()) {
				continue;
			}
			a.Normalize();
			a.mX += (renderer.GetWidth() >> 1);
			a.mY = -a.mY + (renderer.GetHeight() >> 1);

			renderer.DrawPoint(a, mColor, (cShort) mPoints[i].mSelected);
		}
	}
}
cVoid PointsSet::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane, cWinColor color,
						PointsSet::PointPair *tab, cInt tabSize) {
	Point4D a;
	
	if (mVisible) {
		for (cInt i = 0; i < tabSize; i++) {
			a = tab[i].mPoint;
			if (a.mW < plane.GetNearClippingPlane()) {
				continue;
			}
			a.Normalize();
			a.mX += (renderer.GetWidth() >> 1);
			a.mY = -a.mY + (renderer.GetHeight() >> 1);

			renderer.DrawPoint(a, color, (cShort) tab[i].mSelected);
		}
	}
}

/*virtual*/ cVoid PointsSet::Render(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane, cWinColor color) {
	Point4D a;
	
	if (mVisible) {
		for (cInt i = 0; i < mNrOfPoints; i++) {
			a = mPoints[i].mPoint;
			a = matrix * mMatrix * mObjectMatrix * a;
			if (a.mW < plane.GetNearClippingPlane()) {
				continue;
			}
			a.Normalize();
			a.mX += (renderer.GetWidth() >> 1);
			a.mY = -a.mY + (renderer.GetHeight() >> 1);

			renderer.DrawPoint(a, color, (cShort) mPoints[i].mSelected);
		}
	}
}

/*virtual*/ cVoid PointsSet::DisplayInfo(Renderer& renderer, Matrix4D& matrix, ProjectionPlane& plane) { 
	cChar buf[MAX_BUF_SIZE];
	Point4D a;

	if (mDisplayPointsCords) {
		for (cInt i = 0; i < mNrOfPoints; i++) {
			a = mPoints[i].mPoint;
			a = matrix * mMatrix * mObjectMatrix * a;
			if (a.mW < plane.GetNearClippingPlane()) {
				continue;
			}
			a.Normalize();
			a.mX += (renderer.GetWidth() >> 1);
			a.mY = -a.mY + (renderer.GetHeight() >> 1);
			sprintf_s(buf, "[%.0f, %.0f, %.0f]", mPoints[i].mPoint.mX,
					mPoints[i].mPoint.mY,  mPoints[i].mPoint.mZ);
			renderer.DrawString((cInt) a.mX, (cInt) a.mY, buf, 
				(mPoints[i].mSelected == NO_SELECTION) ? false : true, true, false);
		}
	}
}

/*virtual*/ cVoid PointsSet::Transform(Matrix4D& matrix) {
	for (cInt i = 0; i < mNrOfPoints; i++) {
		mPoints[i].mPoint = matrix * mPoints[i].mPoint;
	}
}

/*virtual*/ cVoid PointsSet::Transform(Matrix4D& matrix, cInt iIndex) {
	if (iIndex >= 0 && iIndex < mNrOfPoints) {
		mPoints[iIndex].mPoint = matrix * mPoints[iIndex].mPoint;
	}
}

/*virtual*/ cVoid PointsSet::Transform(Matrix4D& matrix, cBool bRemember) {
}

/*virtual*/ cChar* PointsSet::GetName() {
	return "PointsSet";
}

/*virtual*/ cVoid PointsSet::DisplayInfo(Renderer& renderer) {
	cChar pcBuff [MAX_BUF_SIZE];
	cInt iLineNr = 0;

	sprintf_s(pcBuff, "Points Set Info:");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, RGB(255, 255, 0));
	sprintf_s(pcBuff, "Nr of points :");	
	renderer.DrawString(H_TXT_SPACER, iLineNr * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "%3i", mNrOfPoints);	
	renderer.DrawString(H_TXT_SPACER * 13, iLineNr++ * V_TXT_SPACER, pcBuff, 
		true, true, true, Color::ToWinColor(WHITE));
	iLineNr++;
	sprintf_s(pcBuff, "[Ctrl + V] - Show/Hide Points Coordinates");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "Space - Deselect Points");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "LMB - Select/Deselect Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "RMB - Add Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "DEL - Delete Selected Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
	sprintf_s(pcBuff, "LMB & Drag - Move Selected Point");
	renderer.DrawString(H_TXT_SPACER, iLineNr++ * V_TXT_SPACER, pcBuff, 
		false, true, true, Color::ToWinColor(WHITE));
}